package com.jsj.subway.ai.largemodel;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jsj.subway.ai.largemodel.config.AliConfig;
import com.jsj.subway.ai.largemodel.config.AliIntentConfig;
import com.jsj.subway.ai.largemodel.models.AliModelCompletionRequest;
import com.jsj.subway.ai.largemodel.models.Message;
import com.jsj.subway.ai.largemodel.models.AliModelCompletionResponse;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class AliApiClient implements AliClient {

    @Autowired
    private AliConfig aliConfig;

    @Autowired
    private AliIntentConfig aliIntentConfig;

    @Autowired
    private OkHttpClient defaultOkHttpClient;

    @Autowired
    private OkHttpClient defaultOkHttpsClient;

    public AliApiClient() {
        this.defaultOkHttpsClient = createHttpClient();
    }


    private OkHttpClient createHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[] {
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public Flux<AliModelCompletionResponse> completionsChatByStream(List<Message> messages) {
        sleep();
        AliModelCompletionRequest req = new AliModelCompletionRequest();
        req.setMessages(messages);
        req.setStream(true);
        req.setModel(aliConfig.getModelName());
        req.setTemperature(aliConfig.getTemperature());
        Sinks.Many<AliModelCompletionResponse> sink = Sinks.many().multicast().onBackpressureBuffer();

        completionsChatByStream(req, new ResponseCallback() {
            @Override
            public void onSuccess(AliModelCompletionResponse response) {
                sink.tryEmitNext(response);
            }

            @Override
            public void onFailure(Exception e) {
                AliModelCompletionResponse errorResponse = new AliModelCompletionResponse();
                errorResponse.setErr_code(500);
                errorResponse.setErr_msg(e.getMessage());
                sink.tryEmitNext(errorResponse);
                sink.tryEmitComplete();
            }

            @Override
            public void onComplete() {
                sink.tryEmitComplete();
            }
        });

        return sink.asFlux();
    }

    private void completionsChatByStream(AliModelCompletionRequest request, ResponseCallback callback) {
        RequestBody body = RequestBody.create(
                MediaType.get("application/json; charset=utf-8"),
                JSONUtil.toJsonStr(request)
        );

        Request httpRequest = new Request.Builder()
                .url(aliConfig.getApiHost() + "chat/completions")
                .header(Header.AUTHORIZATION.getValue(), "Bearer " + aliConfig.getApiKey())
                .post(body)
                .build();

        defaultOkHttpClient.newCall(httpRequest).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onFailure(e);
            }

            @Override
            public void onResponse(Call call, Response response) {
                if (!response.isSuccessful()) {
                    callback.onFailure(new IOException("Unexpected code " + response));
                    return;
                }

                try {
                    ResponseBody responseBody = response.body();
                    if (responseBody != null) {
                        BufferedReader reader = new BufferedReader(responseBody.charStream());
                        String line;
                        while ((line = reader.readLine()) != null) {
                            if (line.startsWith("data: ")) {
                                String json = line.substring(6);
                                if (json.equals("[DONE]")) {
                                    callback.onComplete();
                                    break;
                                }
                                AliModelCompletionResponse completionResponse = JSONUtil.toBean(json, AliModelCompletionResponse.class);
                                callback.onSuccess(completionResponse);
                            }
                        }
                        callback.onComplete();
                    }
                } catch (IOException e) {
                    callback.onFailure(e);
                } finally {
                    response.close();
                }
            }


        });
    }


    public AliModelCompletionResponse completionsChat(List<Message> messages) {

        sleep();
        try {
            AliModelCompletionRequest req = new AliModelCompletionRequest();
            req.setMessages(messages);
            req.setStream(false);
            req.setModel(aliIntentConfig.getModelName());
            req.setTemperature(aliIntentConfig.getTemperature());
            return completionsChat(req);
        }catch (Exception e){
            log.error("completionsChat is error:{}",e.getMessage());
            return  new AliModelCompletionResponse();
        }

    }

    private AliModelCompletionResponse completionsChat(AliModelCompletionRequest request) throws IOException {
        RequestBody body = RequestBody.create(
                MediaType.get("application/json; charset=utf-8"),
                JSONUtil.toJsonStr(request)
        );

        Request httpRequest = new Request.Builder()
                .url(aliIntentConfig.getApiHost() + "chat/completions")
                .header(Header.AUTHORIZATION.getValue(), "Bearer " + aliIntentConfig.getApiKey())
                .post(body)
                .build();

        try (Response response = defaultOkHttpsClient.newCall(httpRequest).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }

            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                String json = responseBody.string();
                return JSONUtil.toBean(json, AliModelCompletionResponse.class);
            } else {
                throw new IOException("Response body is null");
            }
        }
    }

    private void sleep(){
        int delay = 1000 + (int)(Math.random() * 2000);
        log.info("sleep time is :{}",delay);
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            log.info("sleep 异常");
        }
    }

}
